#include "stdafx.h"

#include <assert.h>
#include "TextureManager.h"
#include "Model.h"


char modelname[32];
char path[MAX_PATH];

bool ModelLoadFromAsciiMilkshapeStatic(const char* filename,Model* pmodel)
{
	FILE* pFile;
	int numMeshes,numMaterials;
	// int numJoints;


	char* ch = strrchr(filename,'/');
	int len = int(ch-filename+1);
	strncpy(path,filename,len);
	strcpy(modelname,filename+len);

	if(pmodel==NULL)
		return false;

	pFile =  fopen(filename,"r");
	if (pFile==NULL)
	{
		return false;
	}

	char buffer[255];

	// keep reading till the end of the file...
	while(!feof(pFile))
	{
		fgets(buffer,255,pFile);
		
		// skip empty lines (with only newline characters)
		if( strlen(buffer)<2)
			continue;
		// skip comments
		if( strncmp(buffer,"//",2)==0)
			continue;
		// don't read bone information.....
		if( _strnicmp(buffer,"bones",5)==0 )
			continue;
	
		// skip frames for now
		if( _strnicmp(buffer,"frame",5)==0 )
			continue;
		
		// read the meshes
		if(sscanf(buffer,"Meshes: %d",&numMeshes)==1)
		{
			if(numMeshes==0)
				continue;
			else
			{
				if(!ModelReadMeshesMilkshapeAscii(pFile,pmodel,numMeshes))
					return false;
				continue;
			}
		}

		// read the materials
		if(sscanf(buffer,"Materials: %d",&numMaterials)==1)
		{
			if(numMaterials==0)
				continue;
			else
				if(!ModelReadMaterialsMilkshapeAscii(pFile,pmodel,numMaterials))
					return false;
		}
	}

	return true;
}


bool ModelReadMeshesMilkshapeAscii(FILE* pFile,cModel* pmodel,int numMeshes)
{
	float minX,minY,minZ,maxX,maxY,maxZ;
	char name[32];	//dummy name
	int flag,bone,materialIndex,smooth_group;
	float x, y, z, s, t;

	minX = minY = minZ = 100000;
	maxX = maxY = maxZ = -100000;

	// allocate space for the meshes
		
	
	// read the vertices
	for (int i=0; i<numMeshes; i++)
	{
		cModel::Mesh *pmesh = new cModel::Mesh;
		
		fscanf(pFile,"%s %d %d",name,&flag,&materialIndex);
		pmesh->m_name = name;
		pmesh->m_materialIndex = materialIndex;

		fscanf(pFile,"%d",&pmesh->m_numVertices);
		//pmodel->m_numVertices += mesh.m_numVertices;

        for (int j=0; j<pmesh->m_numVertices; j++)
        {
			cModel::Vertex *pvert = new cModel::Vertex;
			
			fscanf(pFile,"%d %f %f %f %f %f %d",
				&flag,&x,&y,&z,&s,&t,&bone);
			//check mins/maxes
			if(minX>x) minX=x;
			if(minY>y) minY=y;
			if(minZ>z) minZ=z;
			if(maxX<x) maxX=x;
			if(maxY<y) maxY=y;
			if(maxZ<z) maxZ=z;
		
			pvert->m_location.Set(x,y,z);
			pvert->m_s = s;
			pvert->m_t = 1-t;
			pvert->m_boneID = bone;

			pmodel->m_vertices.push_back( pvert );
			pmesh->m_vertexIndices.push_back( 
				(int)pmodel->m_vertices.size()-1 );
        }

		// apply min/maxes to model
		pmodel->minX = minX;
		pmodel->minY = minY;
		pmodel->minZ = minZ;
		pmodel->maxX = maxX;
		pmodel->maxY = maxY;
		pmodel->maxZ = maxZ;

		// read normals number
		fscanf(pFile,"%d",&pmesh->m_numNormals);

		for (int j=0; j<pmesh->m_numNormals; j++)
		{
			fscanf(pFile,"%f %f %f",&x,&y,&z);
			Vector3 *pnormal = new Vector3(x,y,z);
			
			pmodel->m_normals.push_back(pnormal);
			pmesh->m_normalIndices.push_back(
				(int)pmodel->m_normals.size()-1 );
		}

		// read triangles number
		fscanf(pFile,"%d",&pmesh->m_numTriangles);

		for (int j=0; j<pmesh->m_numTriangles; j++)
		{
			cModel::Triangle *ptri = new cModel::Triangle;

			fscanf(pFile,"%d %d %d %d %d %d %d %d",
				&flag, 
				&ptri->m_vertexIndices[0],
				&ptri->m_vertexIndices[1],
				&ptri->m_vertexIndices[2],
				&ptri->m_normalIndices[0],
				&ptri->m_normalIndices[1],
				&ptri->m_normalIndices[2],
				&smooth_group);

			pmodel->m_triangles.push_back(ptri);
			pmesh->m_triangleIndices.push_back(
				(int)pmodel->m_triangles.size()-1);
		}

		pmodel->m_meshes.push_back(pmesh);
	}
	return true;
}


bool ModelReadMaterialsMilkshapeAscii(FILE* pFile,cModel* pmodel,int numMaterials)
{
	char buffer[512];
	for (int m=0; m<numMaterials; m++)
	{
		cModel::Material *pmat = new cModel::Material;

		//read the material name
		fscanf(pFile,"%s",buffer);	//buffer now contains the name with quotes
		strncpy(pmat->m_name,buffer+1,strlen(buffer)-2); //copy the name without quotes
		pmat->m_name[strlen(buffer)-2]=0; //null-terminated

		//read ambient values
		fscanf(pFile,"%f %f %f %f",
			&pmat->m_ambient[0],
			&pmat->m_ambient[1],
			&pmat->m_ambient[2],
			&pmat->m_ambient[3]);

		//read diffuse
		fscanf(pFile,"%f %f %f %f",
			&pmat->m_diffuse[0],
			&pmat->m_diffuse[1],
			&pmat->m_diffuse[2],
			&pmat->m_diffuse[3]);

		//specular
		fscanf(pFile,"%f %f %f %f",
			&pmat->m_specular[0],
			&pmat->m_specular[1],
			&pmat->m_specular[2],
			&pmat->m_specular[3]);

		// emissive
		fscanf(pFile,"%f %f %f %f",
			&pmat->m_emissive[0],
			&pmat->m_emissive[1],
			&pmat->m_emissive[2],
			&pmat->m_emissive[3]);
	
		// shininess
		fscanf(pFile,"%f",&pmat->m_shininess);
		//transparency
		fscanf(pFile,"%f",&pmat->m_transparency);

		//color map (the texture)
		fscanf(pFile,"%s",buffer);
		strncpy(pmat->m_pTextureFilename,buffer+1,strlen(buffer)-2);
		pmat->m_pTextureFilename[strlen(buffer)-2]=0; //null-terminated

		//alpha map
		fscanf(pFile,"%s",buffer);
		strncpy(pmat->m_alphaTextureFilename,buffer+1,strlen(buffer)-2);
		pmat->m_alphaTextureFilename[strlen(buffer)-2]=0; //null-terminated		

		strcpy(buffer,path);
		if (strlen(pmat->m_pTextureFilename)>0)
		{
			// if texture name starts with a ".\", skip it
			if(strncmp(pmat->m_pTextureFilename,".\\",2)==0)
				strcat(buffer,pmat->m_pTextureFilename+2);
			else
				strcat(buffer,pmat->m_pTextureFilename);
		}	
		
		HTexture htex = GetTexture(buffer);
		pmodel->m_textures.push_back(htex);
		pmat->m_textureIndex = (int)pmodel->m_textures.size()-1;
		pmodel->m_materials.push_back(pmat);
	}
	return true;
}


bool ModelReloadTextures(cModel* pmodel)
{
	return false;	
}



void ModelApplyMaterial(cModel *pmodel, int matIndex)
{
	assert(0<=matIndex && matIndex<(int)pmodel->m_materials.size());
	const cModel::Material* pmat = pmodel->m_materials[matIndex];

	glMaterialfv(GL_FRONT,	GL_AMBIENT,		pmat->m_ambient);
	glMaterialfv(GL_FRONT,	GL_DIFFUSE,		pmat->m_diffuse);
	glMaterialfv(GL_FRONT,	GL_SPECULAR,	pmat->m_specular);
	glMaterialfv(GL_FRONT,	GL_EMISSION,	pmat->m_emissive);
	glMaterialf (GL_FRONT,	GL_SHININESS,	pmat->m_shininess);

	glColor4fv(pmat->m_diffuse);

	if(pmat->m_textureIndex<0)
	{
		glDisable(GL_TEXTURE_2D);
	}
	else
	{
		glEnable(GL_TEXTURE_2D);
		BindTexture(pmodel->m_textures[pmat->m_textureIndex]);
	}
}


void ModelRenderMesh(cModel *pmodel, int meshIndex)
{
	assert(0<=meshIndex);
	assert(meshIndex<(int)pmodel->m_meshes.size());

	const cModel::Mesh* pmesh = pmodel->m_meshes[meshIndex];

	if(pmesh->m_materialIndex>=-1)
		ModelApplyMaterial(pmodel,pmesh->m_materialIndex);

	glBegin(GL_TRIANGLES);
	{
		int numTri = (int)pmesh->m_triangleIndices.size();
		for(int tr=0; tr<numTri; tr++)
		{
			const cModel::Triangle *ptri = 
				pmodel->m_triangles[pmesh->m_triangleIndices[tr]];

			for (int v=0; v<3; v++)
			{
				cModel::Vertex *pvert = 
					pmodel->m_vertices[pmesh->m_vertexIndices[ptri->m_vertexIndices[v]]];
				Vector3 *pnormal = 
					pmodel->m_normals[pmesh->m_normalIndices[ptri->m_normalIndices[v]]];

				// if animation....
				if (pvert->m_boneID>=0)
				{
					/*
					Matrix mat(joints[vert.bone_id].matrixFinal);
					mat.RotateVector(&norm.n);
					norm.n.Normalize();
					mat.TransformVector(&vert.vec);
					*/
				}

				glTexCoord2f(pvert->m_s, pvert->m_t);
				glNormal3(*pnormal);
				glVertex3(pvert->m_location);
			}
		}
	}
	glEnd();
}



void ModelRender(cModel *pmodel)
{
	assert(pmodel);

	glPushAttrib(GL_ENABLE_BIT | GL_COLOR_BUFFER_BIT);

	int numMeshes = (int)pmodel->m_meshes.size();
	for (int m=0; m<numMeshes; m++)
	{
		ModelRenderMesh(pmodel,m);
	}

	glPopAttrib();
}


//////////////////////////////////////////////////////////////////////////
// model class methods
cModel::cModel()
/*:	m_numMeshes(0),
	m_pMeshes(NULL),
	m_numMaterials(0),
	m_pMaterials(NULL),
	m_numTriangles(0),
	m_pTriangles(NULL),
	m_numVertices(0),
	m_pVertices(NULL)*/
{
	m_meshes.clear();
	m_materials.clear();
	m_triangles.clear();
	m_vertices.clear();
	m_normals.clear();
	m_textures.clear();	
}


cModel::~cModel()
{
	Clear();

	/*for ( i = 0; i < m_numMeshes; i++ )
		delete[] m_pMeshes[i].m_pTriangleIndices;
	for ( i = 0; i < m_numMaterials; i++ )
		delete[] m_pMaterials[i].m_pTextureFilename;

	m_numMeshes = 0;
	if ( m_pMeshes != NULL )
	{
		delete[] m_pMeshes;
		m_pMeshes = NULL;
	}

	m_numMaterials = 0;
	if ( m_pMaterials != NULL )
	{
		delete[] m_pMaterials;
		m_pMaterials = NULL;
	}

	m_numTriangles = 0;
	if ( m_pTriangles != NULL )
	{
		delete[] m_pTriangles;
		m_pTriangles = NULL;
	}

	m_numVertices = 0;
	if ( m_pVertices != NULL )
	{
		delete[] m_pVertices;
		m_pVertices = NULL;
	}*/
}

void cModel::Clear()
{
	// clear meshes
	vector<Mesh*>::iterator imesh = m_meshes.begin();
	while(imesh!=m_meshes.end())
	{
		delete (*imesh);
		imesh++;
	}
	
	// clear materials
	vector<Material*>::iterator imat = m_materials.begin();
	while (imat != m_materials.end()) 
	{
		delete (*imat);
		imat++;
	}

	// clear triangles
	vector<Triangle*>::iterator itri = m_triangles.begin();
	while (itri!=m_triangles.end())
	{
		delete (*itri);
		itri++;
	}

	// clear vertices
	vector<Vertex*>::iterator iver = m_vertices.begin();
	while (iver!=m_vertices.end())
	{
		delete (*iver);
		iver++;
	}

	// clear normals
	vector<Vector3*>::iterator inorm = m_normals.begin();
	while (inorm!=m_normals.end())
	{
		delete (*inorm);
		inorm++;
	}

	// release textures
	vector<HTexture>::iterator itex = m_textures.begin();
	while (itex!=m_textures.end())
	{
		ReleaseTexture(*itex);
		itex++;
	}
}
